#include "GameEditor.h"
#include "OrientationComponent.h"
#include "GraphicalComponent.h"
#include "SelectableComponent.h"
#include "RigidBodyComponent.h"
#include "Library.h"
#include "LightNode.h"
#include "UpdateTask.h"
#include "PhysicsTask.h"
#include "BoundingSphere.h"
#include "Heightmap.h"
#include "LoadTGA.h"
#include "Settings.h"
#include "AIEntityController.h"
#include "Terrain.h"
#include "TriangleMesh.h"
#include "BoundingSphere.h"
#include "IRenderer.h"
#include "MdlFileLoader.h"
#include "ObjModelFile.h"
#include "ModelFactory.h"
#include "MinionBehavior.h"
#include "SpawnPointBehavior.h"
#include "SetDestinationEvent.h"
#include "AddDestinationEvent.h"
#include "DestinationReachedEvent.h"
#include "OnMouseLeftDownEvent.h"
#include "OnMouseLeftUpEvent.h"
#include "OnMouseMoveEvent.h"
#include "OnKeyPressEvent.h"
#include "OnKeyReleaseEvent.h"
#include "TaskManager.h"
#include "Serialization.h"
#include "GameEditorGUI.h"

void GameEditor::CreateEntity()
{
	char buffer[25];
	_itoa_s(m_entityCounter++, buffer, 10);

	OrientationComponent* orientation = 0;
	GraphicalComponent* graphical = 0;
	SelectableComponent* selectable = 0;
	RigidBodyComponent* rigidbody = 0;
	
	Pointer<IResource> resource;
	Pointer<Model> model;
	Pointer<IComponent> component;
	Pointer<Entity> entity = new Entity();

	// OrientationCompenent
	component = orientation = new OrientationComponent(entity);
	orientation->getPosition() = Maths::Vector3D(0, 0, -10);
	entity->RegisterComponent(OrientationComponent::type, component);
	
	// Find Model
	resource = Library::getSingleton().getResource(Model::type, HashedString("Default"));
	CopyPointer(resource, model);

	// GraphicalComponent
	if (model.isValid())
	{
		component = graphical = new GraphicalComponent(entity, model);
		orientation->getTransformNode()->Push_Back(graphical->getNode());
		m_cameraNode->Push_Back(orientation->getTransformNode());

		entity->RegisterComponent(GraphicalComponent::type, component);
	}
	// RigidBodyComponent
	component = rigidbody = new RigidBodyComponent(Pointer<RigidBody>(new RigidBody(1.0f, entity)), entity);
	rigidbody->getPosition() = new DoubleValueTrigger<Maths::Vector3D>(&orientation->getPosition(), &rigidbody->getBody()->getPosition());
	rigidbody->getBody()->AddShape(Pointer<IShape>(new BoundingSphere(1.0f)));
	entity->RegisterComponent(RigidBodyComponent::type, component);

	System* world = PhysicsTask::getSingleton().FindSystem(HashedString("World"));
	if (world)
		world->AddRigidBody(rigidbody->getBody());
	// SelectableComponent
	component = selectable = new SelectableComponent(Pointer<RigidBody>(new RigidBody(1.0f, entity)), entity);
	selectable->getPosition() = new DoubleValueTriggerFunctor<Maths::Vector3D, RigidBody>(&orientation->getPosition(), &selectable->getBody()->getPosition(), selectable->getBody(), &RigidBody::setPosition);
	selectable->getBody()->AddShape(Pointer<IShape>(new BoundingSphere(1.0f)));
	entity->RegisterComponent(SelectableComponent::type, component);

	Game.RegisterSelectable(selectable->getBody());
	Game.RegisterEntity(buffer, entity);

}
void GameEditor::CreateLight()
{	
	LightNode* light;
	Pointer<INode> node = light = new LightNode();
	m_cameraNode->Push_Back(node);
}
void GameEditor::CreateAI()
{	
	AIEntityController* ai;
	Pointer<IEntityController> controller = ai = new AIEntityController(m_selected);

	ai->setWorld(Game.RetrieveWorld("Default"));

	Game.RegisterController(controller);
	UpdateTask::getSingleton().RegisterUpdatable(controller);
}
bool GameEditor::CreateTerrain( const std::string& fileName, const std::string& name, const unsigned int pixelStep, const std::vector<std::string>& textures )
{
	int extIndex = fileName.find_last_of('.');
	std::string extension = fileName.substr(extIndex + 1);

	if (extension == "tga")
	{
		// Load Heightmap
		Pointer<Heightmap> map = new Heightmap();
		if (!LoadTGA(std::string(Settings::getSingleton().getDirectory() + fileName), *map))
			return false;
		map->getPixelStep() = pixelStep; 
		Pointer<Model> terrain = new Terrain(map);
		
		// Add terrain to Library
		Pointer<IResource> resource;
		CopyPointer(terrain, resource);
		Library::getSingleton().AddResource(HashedString(name), resource);

		for (std::vector<std::string>::const_iterator it = textures.begin(); it != textures.end(); ++it)
			terrain->getMeshes()[0]->getMaterial().m_textures.push_back(*it);
		return true;
	}
	return false;
}
void GameEditor::CreateWorld( HashedString name )
{
	Game.CreateWorld(name);
}
Entity* GameEditor::getSelected()
{
	return m_selected;
}
void GameEditor::Select( Entity* entity )
{
	m_selected = entity;
	m_gameEditorGUI->ChangeSelected(m_selected);
}
IEntityController* GameEditor::FindController()
{
	for (IEntityController::Vector_t::iterator it = Game._getControllers().begin(); it != Game._getControllers().end(); ++it)
	{
		if ((*it)->getEntity() == m_selected)
		{
			return (*it);
		}
	}
	return 0;
}
IEntityController* GameEditor::FindController( Entity* entity )
{
	for (IEntityController::Vector_t::iterator it = Game._getControllers().begin(); it != Game._getControllers().end(); ++it)
	{
		if ((*it)->getEntity() == entity)
		{
			return (*it);
		}
	}
	return 0;
}

void GameEditor::AddRigidBodyShape( HashedString shape, float x, float y, float z )
{		
	Pointer<IShape> mesh;
	GraphicalComponent* graphical = (GraphicalComponent*)m_selected->FindComponent(GraphicalComponent::type);
	RigidBodyComponent* rigidbody = (RigidBodyComponent*)m_selected->FindComponent(RigidBodyComponent::type);

	if (shape == BoundingSphere::type || shape == HashedString("Sphere"))
	{
		BoundingSphere* sphere = new BoundingSphere(1.0f);
		mesh = sphere;
	}
	if (shape == TriangleMesh::type)					// Must be static
	{
		ModelNode* modelNode = (ModelNode*)((INode*)graphical->getNode());
		Model* model = modelNode->getModelInstance()->getModel();
		Mesh::Vector_t& meshes = model->getMeshes();

		for (Mesh::Vector_t::iterator it = meshes.begin(); it != meshes.end(); ++it)
		{
			unsigned int offset, size, vertSize = (*it)->getVertexContainer()->getVertexDeclaration().m_vertexSize;
#pragma region FIND_ENUMPOSITION
			{
				const VertexType::Vector_t& params = (*it)->getVertexContainer()->getVertexDeclaration().m_parameters;
				unsigned int paramSize = params.size();

				for (unsigned int i = 0; i < paramSize; ++i)
				{
					if (params[i].m_parameter == enumPosition)
					{
						offset = params[i].m_offset;
						size = params[i].m_size;
						break;
					}
				}
			}
#pragma endregion

			std::vector<float> verts;

			unsigned char* ptr = (unsigned char*)((*it)->getVertexContainer()->getVertexPointer()) + offset;
			unsigned int count = (*it)->getVertexContainer()->getVerticeCount();

			for (unsigned int i = 0; i < count; ++i)		// for each vertex
			{
				for (unsigned int j = 0; j < size; ++j)
					verts.push_back(*(((float*)ptr) + j));
				ptr += vertSize;
			}

			mesh = new TriangleMesh(&verts[0], count/3);
			rigidbody->getBody()->setMass(0.0f);
		}
	}
	if (mesh)
	{
		rigidbody->getBody()->AddShape(mesh);
	}
}
void GameEditor::AddSelectableBodyShape( HashedString shape, float x, float y, float z )
{		
	Pointer<IShape> mesh;
	GraphicalComponent* graphical = (GraphicalComponent*)m_selected->FindComponent(GraphicalComponent::type);
	SelectableComponent* selectable = (SelectableComponent*)m_selected->FindComponent(SelectableComponent::type);

	if (shape == BoundingSphere::type || shape == HashedString("Sphere"))
	{
		BoundingSphere* sphere = new BoundingSphere(1.0f);
		mesh = sphere;
	}
	if (shape == TriangleMesh::type)					// Must be static
	{
		ModelNode* modelNode = (ModelNode*)((INode*)graphical->getNode());
		Model* model = modelNode->getModelInstance()->getModel();
		Mesh::Vector_t& meshes = model->getMeshes();

		for (Mesh::Vector_t::iterator it = meshes.begin(); it != meshes.end(); ++it)
		{
			unsigned int offset, size, vertSize = (*it)->getVertexContainer()->getVertexDeclaration().m_vertexSize;
#pragma region FIND_ENUMPOSITION
			{
				const VertexType::Vector_t& params = (*it)->getVertexContainer()->getVertexDeclaration().m_parameters;
				unsigned int paramSize = params.size();

				for (unsigned int i = 0; i < paramSize; ++i)
				{
					if (params[i].m_parameter == enumPosition)
					{
						offset = params[i].m_offset;
						size = params[i].m_size;
						break;
					}
				}
			}
#pragma endregion

			std::vector<float> verts;

			unsigned char* ptr = (unsigned char*)((*it)->getVertexContainer()->getVertexPointer()) + offset;
			unsigned int count = (*it)->getVertexContainer()->getVerticeCount();

			for (unsigned int i = 0; i < count; ++i)		// for each vertex
			{
				for (unsigned int j = 0; j < size; ++j)
					verts.push_back(*(((float*)ptr) + j));
				ptr += vertSize;
			}

			mesh = new TriangleMesh(&verts[0], count/3);
			selectable->getBody()->setMass(0.0f);
		}
	}
	if (mesh)
	{
		selectable->getBody()->AddShape(mesh);
		if (selectable->getBody()->getSystem() != (System*)(Game._getSelectables()))
			Game.RegisterSelectable(selectable->getBody());
	}
}

bool GameEditor::LoadTexture( std::string filename )
{
	int extIndex = filename.find_last_of('.');
	std::string extension = filename.substr(extIndex + 1);
	int lastWord = filename.find_last_of("\\/");
	std::string name = filename.substr(lastWord + 1, extIndex - lastWord - 1);

	if (extension == "tga")
	{
		// Load Image
		Pointer<Image> image = new Image();
		if (!LoadTGA(std::string(Settings::getSingleton().getDirectory() + filename), *image))
			return false;;					// fail
		Texture texture(image);
		// Add texture to renderer
		Renderer->LoadTexture( HashedString(name), texture, true );
	}
	Game.RegisterTexture(filename);
	return true;
}
bool GameEditor::LoadModel( std::string fileName, std::string& modelName )
{
	int extIndex = fileName.find_last_of('.');
	std::string extension = fileName.substr(extIndex + 1);
	
	if (extension == "obj")
	{
		int lastWord = fileName.find_last_of("\\/");
		std::string name = fileName.substr(lastWord + 1, extIndex - lastWord - 1);

		MdlFile modelFile;
		ObjModelFile file;
		if (LoadObjModelFile((Settings::getSingleton().getDirectory() + fileName).c_str(), file ))
		{
			ConvertToMdlFile(file, modelFile);
			modelFile.modelName = name;
			CreateModelFromMdl(modelFile);

			modelName = modelFile.modelName;
			return true;
		}
	}
	if (extension == "mdl")
	{
		MdlFile modelFile;
		if (LoadMdlFile((Settings::getSingleton().getDirectory() + fileName), modelFile ))
		{
			CreateModelFromMdl(modelFile);
			modelName = modelFile.modelName;
			return true;
		}
	}
	return false;
}
void GameEditor::SetEntityGraphic( Entity* selected, HashedString name )
{
	Pointer<IComponent> component;
	OrientationComponent* orientation = (OrientationComponent*)selected->FindComponent(OrientationComponent::type);
	GraphicalComponent* graphical = (GraphicalComponent*)selected->FindComponent(GraphicalComponent::type);
		
	Pointer<Model> model;
	Pointer<IResource> resource = Library::getSingleton().getResource(Model::type, name);
	if (resource.isValid())				// Found new model?
	{
		CopyPointer(resource, model);
			
		m_selected->UnregisterComponent(GraphicalComponent::type);

		component = graphical = new GraphicalComponent(m_selected, model);
		orientation->getTransformNode()->Push_Back(graphical->getNode());

		m_selected->RegisterComponent(GraphicalComponent::type, component);
	}
}
void GameEditor::SetEntityName( Entity* selected, HashedString name )
{
	Pointer<Entity> entity;

	for (Entity::Map_t::iterator it = Game._getAllEntities().begin(); it != Game._getAllEntities().end(); ++it)
	{
		if (it->second.getPointer() == selected)
		{
			entity = it->second;
			Game._getAllEntities().erase(it->first);
			Game._getAllEntities().insert(Entity::Pair_t(name, entity));
			break;
		}
	}
}
void GameEditor::SetEntityPosition( Entity* selected, float x, float y, float z )
{	
	OrientationComponent* orientation = (OrientationComponent*)m_selected->FindComponent(OrientationComponent::type);
	SelectableComponent* selectable = (SelectableComponent*)m_selected->FindComponent(SelectableComponent::type);
	RigidBodyComponent* rigidbody = (RigidBodyComponent*)m_selected->FindComponent(RigidBodyComponent::type);
		
	if (selectable)
	{
		selectable->setPosition(Maths::Vector3D(x, y, z));
	}
	if (rigidbody)
	{
		rigidbody->setPosition(Maths::Vector3D(x, y, z));
	}
}
bool GameEditor::SetEntityWorld( Entity* selected, HashedString name )
{
	IEntityController* controller = FindController(selected);
	if (controller && controller->Type() == AIEntityController::type)
	{
		AIEntityController* ai = (AIEntityController*)controller;
		Pointer<Pathing::World> world = Game.RetrieveWorld(name);
		ai->setWorld(world);
		return true;
	}
	return false;
}

void GameEditor::SaveGameState( std::string filename )
{
	GameState::Serialize(filename);
}
void GameEditor::LoadGameState( std::string filename )
{
	GameState::Unserialize(filename);
	m_cameraNode = Game._getMainCamera();
}

void GameEditor::DeleteAllSelectableBodyShapes( Entity* selected )
{
	SelectableComponent* selectable = (SelectableComponent*)selected->FindComponent(SelectableComponent::type);

	selectable->getBody()->ClearShapes();
	selectable->getBody()->AddShape(Pointer<IShape>(new BoundingSphere(1.0f)));
}
void GameEditor::DeleteSelectableBodyShape( Entity* selected, unsigned int shape )
{
	SelectableComponent* selectable = (SelectableComponent*)selected->FindComponent(SelectableComponent::type);
		
	selectable->getBody()->RemoveShape(shape);
}
void GameEditor::DeleteAllRigidBodyShapes( Entity* selected )
{
	RigidBodyComponent* selectable = (RigidBodyComponent*)selected->FindComponent(RigidBodyComponent::type);

	selectable->getBody()->ClearShapes();
	selectable->getBody()->AddShape(Pointer<IShape>(new BoundingSphere(1.0f)));
}
void GameEditor::DeleteRigidBodyShape( Entity* selected, unsigned int shape )
{
	RigidBodyComponent* rigidbody = (RigidBodyComponent*)selected->FindComponent(RigidBodyComponent::type);
		
	rigidbody->getBody()->RemoveShape(shape);
}
void GameEditor::SetLightPosition(Pointer<LightNode>& light, float x, float y, float z)
{
	light->getLight().m_Position[0] = x;
	light->getLight().m_Position[1] = y;
	light->getLight().m_Position[2] = z;
}
void GameEditor::RemoveEntity( HashedString name )
{
	Game.RemoveEntity(name);
}
void GameEditor::RemoveEntity( Entity* entity )
{
	Game.RemoveEntity(entity);
}
void GameEditor::AddMinionBehavior( Entity* selected )
{
	IEntityController* controller = FindController(selected);
	if (controller && controller->Type() == AIEntityController::type)
	{
		AIEntityController* ai = (AIEntityController*)(controller);

		Pointer<MinionBehavior> minion = new MinionBehavior(ai);
		Pointer<IBehavior> behavior;
		CopyPointer(minion, behavior);

		ai->Behavior( behavior );
		ai->RegisterHandler(SetDestinationEvent::type, new CEventHandler<MinionBehavior>(minion, &MinionBehavior::Destination));
		ai->RegisterHandler(AddDestinationEvent::type, new CEventHandler<MinionBehavior>(minion, &MinionBehavior::AddDestination));
		ai->RegisterHandler(DestinationReachedEvent::type, new CEventHandler<MinionBehavior>(minion, &MinionBehavior::DestinationReached));
	}
}
void GameEditor::AddSpawnPointBehavior( Entity* selected )
{
	IEntityController* controller = FindController(selected);
	if (controller && controller->Type() == AIEntityController::type)
	{
		AIEntityController* ai = (AIEntityController*)(controller);

		Pointer<SpawnPointBehavior> spawnPoint = new SpawnPointBehavior(ai);
		Pointer<IBehavior> behavior;
		CopyPointer(spawnPoint, behavior);

		ai->Behavior( behavior );
		ai->RegisterHandler(AddDestinationEvent::type, new CEventHandler<SpawnPointBehavior>(spawnPoint, &SpawnPointBehavior::AddDestination));
	}
}

void GameEditor::ToggleAddDestinationFlag()
{
	m_addingDestinationFlag = !m_addingDestinationFlag;
}
void GameEditor::AddDestination( int x, int y )
{
	if (m_selected)
	{
		IEntityController* controller = FindController();
		if (controller && (controller->Type() == AIEntityController::type))
		{
			AIEntityController* ai = (AIEntityController*)controller;
			
			Maths::Vector3D destination;
			Entity* entity = Game.Select(x, y, destination);
			
			if (entity)
			{
				AddDestinationEvent event(destination);
				ai->ReceiveEvent(event);
			}
		}
	}
	m_addingDestinationFlag = false;
}
void GameEditor::SelectEntity( int x, int y )
{
	m_selected = Game.Select(x, y);
	m_gameEditorGUI->ChangeSelected(m_selected);
}
bool GameEditor::ReceiveEvent( IEvent& event )
{
	if (event.Type() == OnMouseLeftDownEvent::type)
	{
		if (m_inRunningGameState)
			return false;

		OnMouseLeftDownEvent& mouseEvent = static_cast<OnMouseLeftDownEvent&>(event);

		int X = mouseEvent.Coords().X;
		int Y = mouseEvent.Coords().Y;
		bool control = mouseEvent.ControlKey();
		bool shift = mouseEvent.ShiftKey();

		if (control && shift)			
		{
		} else if (control)				
		{
		} else if (shift)				
		{
		}else if (m_addingDestinationFlag)
		{
			AddDestination(X, Y);
			return true;
		} else							// No alternate keys pressed select
		{
			SelectEntity(X, Y);
			return true;				// consume event
		}
	} 
	else if (event.Type() == OnMouseMoveEvent::type)
	{
		OnMouseMoveEvent& mouseEvent = static_cast<OnMouseMoveEvent&>(event);

		int X = mouseEvent.Coords().X;
		int Y = mouseEvent.Coords().Y;
		bool control = mouseEvent.ControlKey();
		bool shift = mouseEvent.ShiftKey();
		bool lbutton = mouseEvent.LButton();

		if (control && shift && lbutton)			// Rotate
		{
			Maths::Point<float> movement((float)Maths::DEGTORAD * (-(float)mouseEvent.Movement().Y), (float)Maths::DEGTORAD * -(float)mouseEvent.Movement().X);
			Maths::Vector3D currRotation = m_cameraNode->Rotation();
			currRotation.X() += movement.X;
			currRotation.Y() += movement.Y;
			m_cameraNode->setRotation(currRotation);
			return true;
		} else if (control && lbutton)				// Pan
		{
			Maths::Point<float> movement(-(float)mouseEvent.Movement().X, (float)mouseEvent.Movement().Y);
			Maths::Vector3D position = m_cameraNode->Position() + Maths::Vector3D(movement.X, movement.Y, 0);
			m_cameraNode->setPosition(position);
			return true;
		} else if (shift)							// Rotate
		{
		} else							// No alternate keys pressed select
		{
			return false;				// consume event
		}
	}
	else if (event.Type() == OnKeyPressEvent::type)
	{
		OnKeyPressEvent& keyEvent = static_cast<OnKeyPressEvent&>(event);
		
		if (keyEvent.Key() == 'W')
		{
			Maths::Vector3D distance = Maths::Quaternion::Forward * Maths::Vector3D(0,0,1);
			Maths::Vector3D position = m_cameraNode->Position() + distance;
			m_cameraNode->setPosition(position);
			return true;
		}
		
		if (keyEvent.Key() == 'S')
		{
			Maths::Vector3D distance = Maths::Quaternion::Backward * Maths::Vector3D(0,0,1);
			Maths::Vector3D position = m_cameraNode->Position() + distance;
			m_cameraNode->setPosition(position);
			return true;
		}
		
		if (keyEvent.Key() == '1')
		{
			if (!m_inRunningGameState)
				Run();
			return true;
		}
		if (keyEvent.Key() == '2')
		{
			const float eventTime = (keyEvent.TimeCreated().getSecondsSinceStart());
			const float lastTime = (m_toggleTimer.getSecondsSinceStart() + 0.5f);

			if ( eventTime > lastTime )
			{
				if (!m_inRunningGameState)
				{
					EnterRunningGameState();
					m_inRunningGameState = true;
				} else
				{
					ExitRunningGameState();
					m_inRunningGameState = false;
				}
				m_toggleTimer = keyEvent.TimeCreated();
				return true;
			}
		}
		return false;
	}
	else if (event.Type() == OnKeyReleaseEvent::type)
	{
		OnKeyReleaseEvent& keyEvent = static_cast<OnKeyReleaseEvent&>(event);
	
		if (keyEvent.Key() == '1')
		{
			if (!m_inRunningGameState)
				Stop();
		}
		return true;
	}
	return false;
}
void GameEditor::Run()
{
	if (!m_running)
	{
		// Resume Timer
		TaskManager::getSingleton().ResumeTask(m_timer);
		// Resume Physics
		TaskManager::getSingleton().ResumeTask(m_physics);
		// Resume Updates
		TaskManager::getSingleton().ResumeTask(m_updates);
		// Resume Combat
		TaskManager::getSingleton().ResumeTask(m_combat);
		m_running = true;
	}
}
void GameEditor::Stop()
{
	if (m_running)
	{
		// Suspend Timer
		TaskManager::getSingleton().SuspendTask(m_timer);
		// Suspend Physics
		TaskManager::getSingleton().SuspendTask(m_physics);
		// Suspend Updates
		TaskManager::getSingleton().SuspendTask(m_updates);
		// Suspend Combat
		TaskManager::getSingleton().SuspendTask(m_combat);

		m_running = false;
	}
}

void GameEditor::EnterRunningGameState()
{
	m_gameEditorGUI->wndToolbox->Visible(false);
	m_gameEditorGUI->wndProperties->Visible(false);
	Run();
}
void GameEditor::ExitRunningGameState()
{
	m_gameEditorGUI->wndToolbox->Visible(true);
	m_gameEditorGUI->wndProperties->Visible(true);
	Stop();
}